from collections import deque
from threading import Lock
from quant import utils
from quant.accounts import Order
from quant.const import OrderStatus


class OrderProcessor:
    def __init__(self, account):
        self.account = account
        # self.expired_orders = utils.LimitDict(200)
        # self.done_orders = utils.LimitDict(50)  # {cid: order}
        # self.done_orders = utils.RecentDict(done_order_revive)
        self.done_orders = utils.LimitRecentDict(done_order_limit, done_order_recent)
        self._update_lock = Lock()

    def process_open_orders(self, orders):
        info_engine = self.account.info_engine
        with self._update_lock:
            for o in orders:
                cid = o.client_id
                if is_empty_cid(cid):
                    info_engine.put_order_update_error(Order(), o, 'cid is empty')
                    continue

                if cid in self.account.orders:
                    to_update = self.account.orders[cid]
                    updated = to_update.update(o)
                    if updated:
                        info_engine.put_order_update_detail(to_update, updated)
                else:
                    if cid in self.done_orders:
                        info_engine.put_order_update_error(self.done_orders[cid], o, 'queried order already done')
                    else:
                        # info_engine.put_order_update_error(Order(), o, 'queried order not in account')
                        self.account.orders[cid] = o

    def override_open_orders(self, orders):
        info_engine = self.account.info_engine
        account_orders = self.account.orders

        with self._update_lock:
            account_orders.clear()

            for o in orders:
                cid = o.client_id
                if is_empty_cid(cid):
                    info_engine.put_order_update_error(Order(), o, 'cid is empty')
                    continue
                account_orders[cid] = o

    def update_order(self, order):
        info_engine = self.account.info_engine

        with self._update_lock:
            cid = order.client_id
            if is_empty_cid(cid):
                return info_engine.put_order_update_error(Order(), order, 'cid is empty')

            account_order = self.account.orders.get(order.client_id, None)
            if account_order is None:
                if cid in self.done_orders:
                    done = self.done_orders[cid]
                    if not is_done_order(order):
                        info_engine.put_order_update_error(done, order, 'done order rise again, refused')
                    elif done.status != order.status:
                        info_engine.put_order_update_error(done, order, 'order done reason not consistent')
                    return order.copy()

                info_engine.put_order_update_error(Order(), order, 'updated order not in account')
                account_order = Order()

            update = account_order.update(order)
            if update:
                info_engine.put_order_update_detail(account_order, update)
            self.account.orders[cid] = account_order

            if is_done_order(order):
                self.done_orders[cid] = order
                del self.account.orders[cid]
        return account_order

    def process_place_operation(self, order):
        self.account.orders[order.client_id] = order
        self.account.info_engine.put_place(order)

    def process_cancel_operation(self, order):
        cid = order.client_id
        orders = self.account.orders
        if cid in orders:
            o = orders[cid]
            o.status = OrderStatus.Canceling
        self.account.info_engine.put_cancel(order)


def is_done_order(order):
    return order.status in done_status


def is_empty_cid(cid):
    if cid is None:
        return True
    if cid == '':
        return True
    return False


done_status = {
    OrderStatus.PlaceFailed,
    OrderStatus.Canceled,
    OrderStatus.FullyFilled,
}
done_order_recent = 2
done_order_limit = 20














